//调用安卓原生方法集合
const main = plus.android.runtimeMainActivity();
const Intent = plus.android.importClass('android.content.Intent');
const Build = plus.android.importClass('android.os.Build');
const Uri = plus.android.importClass('android.net.Uri');
const Settings = plus.android.importClass('android.provider.Settings');
const DocumentsContract = plus.android.importClass('android.provider.DocumentsContract');
const ContentUris = plus.android.importClass('android.content.ContentUris');
const MediaStore = plus.android.importClass('android.provider.MediaStore');
const Environment = plus.android.importClass('android.os.Environment');
const System = plus.android.importClass('java.lang.System');
const File = plus.android.importClass('java.io.File');
const FileInputStream = plus.android.importClass('java.io.FileInputStream')
const FileOutputStream = plus.android.importClass('java.io.FileOutputStream')
const OutputStreamWriter = plus.android.importClass('java.io.OutputStreamWriter')
const BufferedWriter = plus.android.importClass('java.io.BufferedWriter')
const Byte = plus.android.importClass("java.lang.Byte");
const Array = plus.android.importClass("java.lang.reflect.Array");
const FileChannel = plus.android.importClass("java.nio.channels.FileChannel");
const StandardOpenOption = plus.android.importClass("java.nio.file.StandardOpenOption");
const MediaScannerConnection = plus.android.importClass('android.media.MediaScannerConnection')
const Context = plus.android.importClass('android.content.Context');
const URLConnection = plus.android.importClass('java.net.URLConnection');
const MimeTypeMap = plus.android.importClass("android.webkit.MimeTypeMap")
const WallpaperManager = plus.android.importClass("android.app.WallpaperManager")
const BitmapFactory = plus.android.importClass("android.graphics.BitmapFactory")
const AudioManager = plus.android.importClass('android.media.AudioManager');
const resolver = main.getContentResolver()
const rootPath = Environment.getExternalStorageDirectory()
const sdPath = System.getenv("SECONDARY_STORAGE");
const isKitKat = Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT;

/**
 * 获取文件后缀名
 * @param {String} text 带后缀名的字符串
*/
function getSuffix (text) {
	const fileName = text.lastIndexOf(".");
	const fileNameLength = text.length;
	const fileFormat = text.substring(fileName + 1, fileNameLength);
	return fileFormat.split('?')[0];
}


/**
 * 处理系统文件管理器导出的文件
 * @param {Uri} uri 安卓Uri对象
*/
async function handleFile (uri) {
	plus.android.importClass(uri);
	const scheme = uri.getScheme()
	let filePath = ''
	const isDocument = DocumentsContract.isDocumentUri(main, uri)
	const isTreeDocument = DocumentsContract.isTreeUri(uri)
	// DocumentProvider
	if(isKitKat && (isDocument || isTreeDocument)) {
		const authority = uri.getAuthority()
	    // console.log("版本大于 4.4 ");
	    // ExternalStorageProvider
	    if("com.android.externalstorage.documents" == authority) {
	        const docId = isDocument ? DocumentsContract.getDocumentId(uri) : DocumentsContract.getTreeDocumentId(uri);
	        const split = docId.split(":");
	        const type = split[0];
	        if("primary" == type) {
	            filePath = rootPath + "/" + split[1]
	        } else {
	            if(sdPath) filePath = sdPath + "/" + split[1]
	        }
	    }
	    // DownloadsProvider
	    else if("com.android.providers.downloads.documents" == authority) {
	        const docId = isDocument ? DocumentsContract.getDocumentId(uri) : DocumentsContract.getTreeDocumentId(uri);
			const split = docId.split(":");
			filePath = isDocument ? split[1] : rootPath + '/' + split[0]
	        // const contentUri = ContentUris.withAppendedId(
	        //    Uri.parse("content://downloads/public_downloads"), Long.valueOf(id));
	        // Uri.parse("content://downloads/public_downloads"), id);
	        // filePath = getDataColumn(contentUri, null, null)
	    }
	    // MediaProvider
	    else if("com.android.providers.media.documents" == authority) {
	        const docId = isDocument ? DocumentsContract.getDocumentId(uri) : DocumentsContract.getTreeDocumentId(uri);
	        const split = docId.split(":");
	        const type = split[0];
			let contentUri = ''
	        if("image" == type) {
	        contentUri = MediaStore.Images.Media.EXTERNAL_CONTENT_URI;
	        } else if("video" == type) {
	        contentUri = MediaStore.Video.Media.EXTERNAL_CONTENT_URI;
	        } else if("audio" == type) {
	        contentUri = MediaStore.Audio.Media.EXTERNAL_CONTENT_URI;
	        } else if("document" == type) {
	        contentUri = MediaStore.Files.getContentUri('external');
	        }
	        const selection = "_id=?";
	        const selectionArgs = new Array();
	        selectionArgs[0] = split[1];
	        filePath = getDataColumn(contentUri, selection, selectionArgs)
			// if ( !filePath ) {
			// 	const inputStream = resolver.openInputStream(uri)
			// 	const arrayBuffer = await inputStreamToArrayBufferChunked(inputStream)
			// 	return {
			// 		type: 'ArrayBuffer',
			// 		data: arrayBuffer
			// 	}
			// }
	    }
	}
	// MediaStore (and general)
	else if("content" == scheme) {
	    filePath = getDataColumn(uri, null, null)
	}
	// File
	else if("file" == scheme) {
	    filePath = uri.getPath()
	}
	const file = new File(filePath)
	const fileSize = isTreeDocument ? '' : new FileInputStream(file)
	const available = isTreeDocument ? '' : fileSize.available()
	const lastModified = file.lastModified()
	return isTreeDocument ? {
		path: filePath,
		name: file.getName(),
		lastModified: lastModified,
		time: dateFormat(lastModified)
	} : {
		path: filePath,
		name: file.getName(),
		type: plus.android.invoke(resolver, "getType",uri) || getFileMimeTypeAndroid(file),
		available: available,
		size: readFileSize(available),
		lastModified: lastModified,
		time: dateFormat(lastModified)
	}
	
}

function inputStreamToArrayBufferChunked(inputStream, chunkSize = 1024 * 64) {
  return new Promise((resolve, reject) => {
    try {
      const chunks = [];
      let totalLength = 0;
	  const buffer = Array.newInstance(Byte.TYPE, chunkSize);
      function readChunk() {
        const length = inputStream.read(buffer)
        if (length > 0) {
          // 处理当前块
          const chunkBuffer = new ArrayBuffer(length);
          const chunkView = new Uint8Array(chunkBuffer);
          
          for (let i = 0; i < length; i++) {
            chunkView[i] = plus.android.invoke(buffer, "get", i)
          }
          chunks.push(chunkView);
          totalLength += length;
          
          // 继续读取下一块
          setTimeout(readChunk, 0); // 避免阻塞UI
        } else {
		  inputStream.close()
          // 读取完成
          const arrayBuffer = new ArrayBuffer(totalLength);
          const resultView = new Uint8Array(arrayBuffer);
          let offset = 0;
          for (const chunk of chunks) {
            resultView.set(chunk, offset);
            offset += chunk.length;
          }
          resolve(arrayBuffer);
        }
      }
      readChunk();
    } catch (error) {
	  inputStream.close()
      reject(error);
    }
  });
}

/**
 * 根据文件后缀获取MIME类型
 * @param {Uri} file 安卓file对象
*/
function getFileMimeTypeAndroid(file) {
  const fileName = file.getName();
  const fileFormat = getSuffix(fileName);
  const extension = fileFormat.toLowerCase();
  const mimeTypeMap = MimeTypeMap.getSingleton();
  let mimeType = mimeTypeMap.getMimeTypeFromExtension(extension);
  if (!mimeType) mimeType = URLConnection.guessContentTypeFromName(fileName);
  return mimeType || 'application/octet-stream';
}

function getDataColumn(uri, selection, selectionArgs) {
    plus.android.importClass(resolver);
    const cursor = resolver.query(uri, ['_data'], selection, selectionArgs, null);
    if(cursor != null) {
		plus.android.importClass(cursor)
		if( cursor.moveToFirst() ) {
			const column_index = cursor.getColumnIndexOrThrow('_data');
			const result = cursor.getString(column_index)
			cursor.close();
			return result;
		}
    }
    return null;
}

function readFileSize (available) {
	const size = available != 'null' ? available : 0;
	let fileSizeString;
	if(size == 0){
		fileSizeString = "0B";
	}else if(size < 1024){
		fileSizeString = size + "B";
	}else if(size < 1048576){
		fileSizeString = (size/1024).toFixed(2) + "KB";
	}else if (size < 1073741824){
		fileSizeString = (size/1048576).toFixed(2) + "MB";
	}else{
		fileSizeString = (size/1073741824).toFixed(2) + "GB";
	}
	return fileSizeString;
}

function dateFormat (lastModified) {
	const d = new Date(lastModified)
	const year = d.getFullYear()
	const month = d.getMonth() + 1
	const day = d.getDate()
	const hour = d.getHours()
	const seconds = d.getSeconds()
	const minutes = d.getMinutes()
	return year + '年' + month + '月' + day + '日 ' + hour + '时' + seconds + '分' + minutes + '秒'
}

/**
 * 判断是否有权限
 * @param {Array} permissions 权限列表
 * @param {String} message 无权限提示
 * @param {Function} success 成功回调
 * @param {Function} fail 失败回调
*/
const requestPermissions = function ({permissions = [], message = '未开启权限', success, fail}) {
	plus.android.requestPermissions(permissions,(e) =>{
		if(e.deniedAlways.length>0){//权限被永久拒绝
			uni.showModal({
				content: message + '\n\n相关权限已被永久拒绝\n\n' + '点击确认跳转应用设置手动开启权限',
				success: ret => {
					if ( ret.confirm ) {
						//权限被永久拒绝，打开设置窗口
						openSetting()
					} else {
						fail && fail('权限被永久拒绝')
					}
				},
				fail: () => {
					fail && fail('权限被永久拒绝')
				}
			})
		}
		if(e.deniedPresent.length>0){//权限被临时拒绝
			uni.showModal({
				content: message + '\n\n相关权限已被临时拒绝\n\n' + '点击确认重新申请权限',
				success: ret => {
					if ( ret.confirm ) {
						requestPermissions({permissions, message, success, fail})
					} else {
						fail && fail('权限被临时拒绝')
					}
				},
				fail: () => {
					fail && fail('权限被临时拒绝')
				}
			})
		}
		//权限被允许
		if(e.granted.length > 0)success && success()
	}, function(e){
		fail && fail(e)
	});
}

/**
 * 调用系统分享分享文本内容
 * @param {String} content 文本内容
*/
const shareText = function (content) {
	const sendIntent = new Intent();
	sendIntent.setAction(Intent.ACTION_SEND);
	sendIntent.putExtra(Intent.EXTRA_TEXT, content);
	sendIntent.setType("text/plain");
	// 创建选择器，让用户选择分享目标
	const shareIntent = Intent.createChooser(sendIntent, "分享到");
	main.startActivity(shareIntent);
}

/**
 * 调用系统分享分享文件
 * @param {String} filePath 文件路径
 * @param {Function} success 成功回调
 * @param {Function} fail 失败回调
*/
const shareFile = function (data) {
	const params = typeof data == 'string' ? {filePath: data} : data
	const { filePath, success, fail } = params
	if ( !filePath ) {
		uni.showToast({
			title: '请传入分享文件路径',
			icon: 'none'
		})
		return
	}
	requestPermissions({
		permissions: ['android.permission.READ_EXTERNAL_STORAGE', 'android.permission.WRITE_EXTERNAL_STORAGE'],
		message: '分享本地文件需要读取和写入存储权限',
		success: () => {
			const url = plus.io.convertLocalFileSystemURL(filePath)
			const file = new File(url)
			if ( file && file.exists() ) {
				const name = file.getName()
				const fileFormat = getSuffix(name);
				const accept = MimeTypeMap.getSingleton().getMimeTypeFromExtension(fileFormat)
				const listener = new plus.android.implements('android.media.MediaScannerConnection$OnScanCompletedListener', {
				    'onScanCompleted': (path, uri) => {
				        const shareIntent = new Intent()  
				            .setAction(Intent.ACTION_SEND)  
				            .setType(accept || "*/*")  
				            .setFlags(Intent.FLAG_ACTIVITY_NEW_TASK)  
				            .putExtra(Intent.EXTRA_TEXT, '分享到')  
				            .putExtra(Intent.EXTRA_STREAM, uri) // Uri.parse(file_entry.fullPath))  
				            .addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION | Intent.FLAG_GRANT_WRITE_URI_PERMISSION)
						main.startActivity(Intent.createChooser(shareIntent, '分享到'))
						success && success({
							status: 'ok',
							message: '成功打开分享窗口，注意这不代表分享已经完成',
							filePath: path
						})
				        // new File(path).deleteOnExit()//让复制的文件内存回收后自动删除，但没有效果，暂时保留
				    }  
				})
				if ( filePath.startsWith('_doc') || filePath.indexOf('Android/data') > -1 ) {//是私有目录的文件
					const TempPath = rootPath + '/Download/temp'//在android10以上私有目录文件无法分享，需要将私有目录文件移动到公共文件夹，再去分享
					uni.showLoading({ title: '正在复制文件' })
					copyFileWithNIO({//使用NIO复制文件到公共目录
						filePath,
						targetPath: TempPath,
						success: res => {
							uni.hideLoading()
							MediaScannerConnection.scanFile(main, [res.savedFilePath], null, listener)
						},
						fail: err => {
							uni.hideLoading()
							uni.showToast({
								title: JSON.stringify(err),
								icon: 'none'
							})
							fail && fail(err)
						}
					})
				} else {
					file.renameTo(new File(filePath))
					MediaScannerConnection.scanFile(main, [filePath], null, listener)
				}
			} else {
				uni.showToast({
					title: '获取资源失败',
					icon: 'none'
				})
			}
		},
		fail: (err) => {
			fail && fail({
				status: 'requestPermissions::fail',
				message: '获取文件读写权限失败'
			})
		}
	})
}

/**
 * 开启原生系统文件管理器
 * @param {String} accept 文件类型 不传默认选择文件夹 指定
 * @param {Boolean} multiple 开启多选模式
 * @param {Number} count 最大处理文件数量 不传默认全部
 * @param {Function} success 成功回调
 * @param {Function} fail 失败回调
*/
const openFileSystem = function ({accept = '', multiple, count, success, fail}) {
	const REQUESTCODE = 1;
	const intent = accept ? new Intent(Intent.ACTION_GET_CONTENT) : new Intent(Intent.ACTION_OPEN_DOCUMENT_TREE);
	if ( accept ) {//选择文件
		//API大于19之后设置多个类型采用以下方式
		const mimeTypes = accept.split('|')
		intent.addCategory(Intent.CATEGORY_OPENABLE);
		if ( multiple ) intent.putExtra(Intent.EXTRA_ALLOW_MULTIPLE, true)//允许多选
		if ( mimeTypes.length > 1 && isKitKat ) {//指定多类型文件
			intent.setType(accept)
			intent.putExtra(Intent.EXTRA_MIME_TYPES, mimeTypes)
		}  else {
			intent.setType(accept)
		}
	} else {//选择文件夹
		intent.addCategory(Intent.CATEGORY_DEFAULT)
	}
	//intent.setType("*/*");//设置类型，任意类型
	//intent.setType("video/*|audio/*");指定多种类型
	//intent.setType("image/*");选择图片
	//intent.setType("audio/*"); //选择音频
	//intent.setType("video/*"); //选择视频 （mp4 3gp 是android支持的视频格式）
	//intent.setType("application/x-mpegURL"); //选择m3u8 
	main.onActivityResult = async function(requestCode, resultCode, data) {
	    if (resultCode == -1 && REQUESTCODE == requestCode) {
			try{
				if ( data.getData() != null ) {
					const file = await handleFile(data.getData())
					success && success(file)
					success = null
				} else if ( data.getClipData() != null ) {
					const clipData = data.getClipData()
					plus.android.importClass(clipData);
					const len = count ? Math.min(clipData.getItemCount(), count) : clipData.getItemCount()
					uni.showLoading({
						title: '正在读取选中的' + len + '个文件'
					})
					const files = []
					for ( let i = 0; i < len; i++ ) {
						const clipItem = clipData.getItemAt(i)
						const uri = clipItem.getUri()
						files.push(await handleFile(uri))
					}
					uni.hideLoading()
					success && success(files)
					success = null
				}
			}catch(e){
				fail && fail(e)
			}
	    } else {
			fail && fail('no file selected')
		}
	}
	main.startActivityForResult(intent,REQUESTCODE);
}

/**
 * 设置系统锁屏和壁纸
 * @param {String} type 类型 all-锁屏和壁纸 wallpaper-壁纸 lock-锁屏
 * @param {String} path 图片路径
*/
const setWallpaper = function (type = 'all', path) {
	try {
		const filePath = plus.io.convertLocalFileSystemURL(path)
		const bitmap = BitmapFactory.decodeFile(filePath)
		const wallpaperManager = WallpaperManager.getInstance(main)
		plus.android.importClass(wallpaperManager)
		if ( type == 'all' || type == 'wallpaper' ) wallpaperManager.setBitmap(bitmap) //设置壁纸
		if ( type == 'all' || type == 'lock' ) wallpaperManager.setBitmap(bitmap, null, true, WallpaperManager.FLAG_LOCK) //设置锁屏
		plus.android.invoke(bitmap, 'recycle')
		uni.showToast({
			title: "壁纸设置成功",
			icon: "none"
		})
	} catch (e) {
		uni.showToast({
			title: "壁纸设置失败",
			icon: "none"
		})
	}
}

/**
 * 卸载应用
 * @param {String} packagename 应用包名
*/
const uninstall = function (packagename) {
	const uri = Uri.fromParts('package', packagename, null)
	const intent = new Intent(Intent.ACTION_DELETE, uri)
	main.startActivity(intent)
}

/**
 * 写入文档内容
 * @param {String} filePath 文件路径
 * @param {String} content 内容
 * @param {String} charset 字符编码
 * @param {Function} success 成功回调
 * @param {Function} fail 失败回调
*/
const writeDocumentContent = function ({filePath, content, charset, success, fail}) {
	const file = new File(filePath)
	if ( file != null && file.exists() ) {
		try{
			file.createNewFile()
			const fileOutputStream = new FileOutputStream(file)
			const outputStreamWriter = new OutputStreamWriter(fileOutputStream, charset || 'GB2312')
			const bufferedWriter = new BufferedWriter(outputStreamWriter)
			bufferedWriter.write(content)
			bufferedWriter.close()
			const fileSize = new FileInputStream(file)
			const available = fileSize.available()
			const size = readFileSize(available)
			success && success({
				available: available,
				size: size
			})
		}catch(e){
			fail && fail(e)
		}
	} else {
		fail && fail({
			message: '文件不存在或者不能被操作'
		})
	}
}

/**
 * 下载网络资源存入本地
 * @param {String} src 网络图片路径
 * @param {Array} referer referer
 * @param {String} format 图片格式
 * @param {Function} success 成功回调
 * @param {Function} fail 失败回调
*/
const downloadNetResourceSaveLocal = function ({src, referer, success, fail, complete}) {
	uni.downloadFile({
		header: {
			Referer: referer || src
		},
		url: src,
		success: res => {
			if (res.statusCode === 200) {
				const result = {
					errCode: 0,
					errMsg: 'ok',
					tempFilePath: res.tempFilePath
				}
				success && success(result)
				complete && complete(result)
			} else {
				const result = {
					errCode: 2,
					errMsg: '下载失败'		
				}
				fail && fail(result)
				complete && complete(result)
			}
		},
		fail: err => {
			const result = {
				errCode: 1,
				errMsg: err.toString()		
			}
			fail && fail(result)
			complete && complete(result)
		}
	})
}

/**
 * 下载网络资源并转化为base64
 * @param {String} src 网络路径
 * @param {Array} referer 来源
*/
const downloadNetResourceToBase64 = function ({src, referer}) {
	return new Promise((resolve, reject) => {
		uni.request({
			url: src,
			header: {
				Referer: referer || src
			},
			method: 'GET',
			responseType: 'arraybuffer',
			success: res => {
				const base64 = uni.arrayBufferToBase64(res.data)
				resolve(base64)
			},
			fail: err => {
				reject(err)
		    }
		})
	})
}

/**
 * 设置全屏
 * @param {String} bol 布尔值
*/
const setFullscreen = function (bol) {
	plus.navigator.setFullscreen(bol);
}

/**
 * 写入并导出文件
 * @param {String} fileName 导出文件名称
 * @param {Array} content 写入文件内容
 * @param {Function} success 成功回调
 * @param {Function} fail 失败回调
*/
const writeAndExportFile = function ({fileName, content, success, fail}) {
	plus.io.requestFileSystem(plus.io.PRIVATE_DOC, ( fs ) => {
		const file = new File(fs.root.fullPath + fileName)
		file.createNewFile()
		let fileOutputStream = new FileOutputStream(file)
		let outputStreamWriter = new OutputStreamWriter(fileOutputStream, 'utf-8')
		let bufferedWriter = new BufferedWriter(outputStreamWriter)
		bufferedWriter.write(content)
		bufferedWriter.close()
		outputStreamWriter.close()
		fileOutputStream.close()
		bufferedWriter = null
		outputStreamWriter = null
		fileOutputStream = null
		success && success({
			fileName: file.getName(),
			root: fs.root.fullPath
		})
	}, ( e ) => {
		fail && fail(e)
	});
}

/**
 * 重命名和移动文件
 * @param {String} filePath 文件路径
 * @param {String} targetPath 要移动过去的文件夹路径
 * @param {String} newName 新名称
 * @param {Function} success 成功回调
 * @param {Function} fail 失败回调
*/
const renameAndMoveFile = function ({filePath, targetPath, newName, success, fail}) {
	if ( !filePath ) {
		uni.showToast({
			title: '请传入文件路径',
			icon: 'none'
		})
		return
	}
	if ( !targetPath && !newName ) {
		uni.showToast({
			title: '移动路径和新名称至少需要传一个',
			icon: 'none'
		})
		return
	}
	requestPermissions({
		permissions: ['android.permission.READ_EXTERNAL_STORAGE', 'android.permission.WRITE_EXTERNAL_STORAGE'],
		message: '重命名和移动文件需要读取和写入存储权限',
		success: () => {
			const url = plus.io.convertLocalFileSystemURL(filePath)
			const file = new File(url)
			if ( file && file.exists() ) {
				const fileName = file.getName()
				const fileFormat = getSuffix(fileName);
				const newFileName = newName ? newName.replace(/[\r\n\s#]+/g, '') + '.' + fileFormat : fileName
				const targetDir = new File(targetPath)
				const newPath = targetPath + '/' + newFileName
				if ( !targetDir.exists() ) targetDir.mkdirs()
				const bol = file.renameTo(new File(newPath))
				if ( bol ) {
					success && success({
						status: 'ok',
						message: '复制成功',
						savedFilePath: newPath
					})
				} else {
					fail && fail({
						status: 'file.renameTo::fail',
						message: '复制文件失败'
					})
				}
			} else {
				fail && fail({
					status: 'file.exits::fail',
					message: '获取文件资源失败，请检查文件路径是否正确'
				})
			}
		},
		fail: () => {
			fail && fail({
				status: 'requestPermissions::fail',
				message: '获取文件读写权限失败'
			})
		}
	})
}

/**
 * 复制文件到指定路径
 * @param {String} filePath 文件路径
 * @param {String} targetPath 要复制过去的文件夹路径
 * @param {Function} success 成功回调
 * @param {Function} fail 失败回调
*/
const copyFile = function ({filePath, targetPath, success, fail}) {
	if ( !filePath ) {
		uni.showToast({
			title: '请传入文件路径',
			icon: 'none'
		})
		return
	}
	if ( !targetPath ) {
		uni.showToast({
			title: '请传入复制文件夹路径',
			icon: 'none'
		})
		return
	}
	requestPermissions({
		permissions: ['android.permission.READ_EXTERNAL_STORAGE', 'android.permission.WRITE_EXTERNAL_STORAGE'],
		message: '复制文件需要读取和写入存储权限',
		success: () => {
			const url = plus.io.convertLocalFileSystemURL(filePath)
			const file = new File(url)
			if ( file && file.exists() ) {
				const targetDir = new File(targetPath);
				const targetFile = new File(targetDir, file.getName()); // 目标文件路径
				// 确保目标文件夹存在
				if (!targetDir.exists()) targetDir.mkdirs();
				const ins = new FileInputStream(file);
				const out = new FileOutputStream(targetFile)
				const buffer = Array.newInstance(Byte.TYPE, 1024);
				let length
				while ((length = ins.read(buffer)) > 0) {
				    out.write(buffer, 0, length);
				}
				success && success({
					status: 'ok',
					message: '复制文件成功',
					savedFilePath: targetPath + '/' + file.getName()
				})
			} else {
				fail && fail({
					status: 'file.exits::fail',
					message: '获取文件资源失败，请检查文件路径是否正确'
				})
			}
		},
		fail: () => {
			fail && fail({
				status: 'requestPermissions::fail',
				message: '获取文件读写权限失败'
			})
		}
	})
}

/**
 * 使用NIO复制文件
 * @param {String} filePath 文件路径
 * @param {String} targetPath 要复制过去的文件夹路径
 * @param {Function} success 成功回调
 * @param {Function} fail 失败回调
*/
const copyFileWithNIO = function ({filePath, targetPath, success, fail}) {
	if ( !filePath ) {
		uni.showToast({
			title: '请传入文件路径',
			icon: 'none'
		})
		return
	}
	if ( !targetPath ) {
		uni.showToast({
			title: '请传入复制文件夹路径',
			icon: 'none'
		})
		return
	}
	requestPermissions({
		permissions: ['android.permission.READ_EXTERNAL_STORAGE', 'android.permission.WRITE_EXTERNAL_STORAGE'],
		message: '复制文件需要读取和写入存储权限',
		success: () => {
			const url = plus.io.convertLocalFileSystemURL(filePath)
			const file = new File(url)
			if ( file && file.exists() ) {
				let inChannel = null;
				let outChannel = null;
				let fis = null;
				let fos = null;
				try {
					const targetDir = new File(targetPath)
					// 确保目标文件夹存在
					if (!targetDir.exists()) targetDir.mkdirs();
					const targetFile = new File(targetDir, file.getName()); // 目标文件路径
				    fis = new FileInputStream(file);
				    fos = new FileOutputStream(targetFile);
				    inChannel = fis.getChannel();
				    outChannel = fos.getChannel();
				    // 直接传输（零拷贝技术）
				    const size = inChannel.size();
				    let position = 0;
				    const chunkSize = 16 * 1024 * 1024; // 每次传输16MB
				    while (position < size) {
				        position += inChannel.transferTo(position, chunkSize, outChannel);
				        const progress = Math.round((position / size) * 100);
				        // console.log(`NIO复制进度: ${progress}%`);
				    }
					success && success({
						status: 'ok',
						message: '复制文件成功',
						savedFilePath: targetPath + '/' + file.getName()
					})
				} catch (e) {
					fail && fail({
						status: 'copy::fail',
						message: '复制文件失败::' + e.toString()
					})
				} finally {
				    try {
				        if (inChannel) inChannel.close();
				        if (outChannel) outChannel.close();
				        if (fis) fis.close();
				        if (fos) fos.close();
				    } catch (closeErr) {
				        // console.error("关闭通道时出错:", closeErr);
				    }
				}
			} else {
				fail && fail({
					status: 'file.exits::fail',
					message: '获取文件资源失败，请检查文件路径是否正确'
				})
			}
		},
		fail: () => {
			fail && fail({
				status: 'requestPermissions::fail',
				message: '获取文件读写权限失败'
			})
		}
	})
}

/**
 * 删除文件
 * @param {String} filePath 需要删除的文件路径
 * @param {Function} success 成功回调
 * @param {Function} fail 失败回调
*/
const deleteFile = function ({filePath, success, fail}) {
	if ( !filePath ) {
		uni.showToast({
			title: '请传入文件路径',
			icon: 'none'
		})
		return
	}
	requestPermissions({
		permissions: ['android.permission.READ_EXTERNAL_STORAGE', 'android.permission.WRITE_EXTERNAL_STORAGE'],
		message: '删除文件需要读取和写入存储权限',
		success: () => {
			const url = plus.io.convertLocalFileSystemURL(filePath)
			const file = new File(url)
			if ( file && file.exists() ) {
				file.delete()
				success && success({
					status: 'ok',
					message: '成功删除文件'
				})
			} else {
				fail && fail({
					status: 'file.exits::fail',
					message: '获取文件资源失败，请检查文件路径是否正确'
				})
			}
		},
		fail: () => {
			fail && fail({
				status: 'requestPermissions::fail',
				message: '获取文件读写权限失败'
			})
		}
	})
}

/**
 * 删除文件集合
 * @param {String} filePaths 需要删除的文件路径集合
 * @param {Function} success 成功回调
 * @param {Function} fail 失败回调
*/
const deleteFiles = function ({filePaths, success, fail}) {
	if ( !filePaths || filePaths.length == 0 ) {
		uni.showToast({
			title: '请传入文件路径集合',
			icon: 'none'
		})
		return
	}
	requestPermissions({
		permissions: ['android.permission.READ_EXTERNAL_STORAGE', 'android.permission.WRITE_EXTERNAL_STORAGE'],
		message: '删除文件需要读取和写入存储权限',
		success: () => {
			const successList = [], failList = []
			filePaths.forEach(filePath => {
				const url = plus.io.convertLocalFileSystemURL(filePath)
				const file = new File(url)
				if ( file && file.exists() ) {
					file.delete()
					successList.push(filePath)
				} else {
					failList.push(filePath)
				}
			})
			success && success({
				status: 'ok',
				message: failList.length == 0 ? '删除文件成功' : '删除文件成功，但部分文件删除失败',
				deleteSuccessList: successList,//删除成功文件集合
				deleteFailList: failList//删除失败文件集合
			})
		},
		fail: () => {
			fail && fail({
				status: 'requestPermissions::fail',
				message: '获取文件读写权限失败'
			})
		}
	})
}

/**
 * 设置系统音量
 * @param {String} volume 需要设置的音量 范围0-1
 * @param {Number} mode 音量模式 0-媒体 1-铃声 2-闹钟 3-通知 4-系统
 * @param {String} flag 标志位 UI-表示显示系统的音量对话框，sound-表示调整音量时播放声音
*/
const setVolume = function (volume, mode = 0, flag) {
	const modes = [
		AudioManager.STREAM_MUSIC,//媒体（音乐、视频）音量
		AudioManager.STREAM_RING,//铃声音量
		AudioManager.STREAM_ALARM,//闹钟音量
		AudioManager.STREAM_NOTIFICATION,//通知音量
		AudioManager.STREAM_SYSTEM//系统音量
	]
	const audioManager = main.getSystemService(Context.AUDIO_SERVICE);
	const maxVolume = audioManager.getStreamMaxVolume(modes[mode])//获取设备最大音量级别
	const volumeLevel = Number((maxVolume * volume).toFixed(0))//根据最大音量级别计算设置的音量级别（只能为整数）
	// 设置媒体音量模式
	audioManager.setMode(AudioManager.MODE_IN_COMMUNICATION);
	flag = flag == 'UI' ? AudioManager.FLAG_SHOW_UI : 'sound' ? AudioManager.FLAG_PLAY_SOUND : null
	audioManager.setStreamVolume(
	  modes[mode], // 指定媒体流类型
	  volumeLevel, // 音量级别
	  flag// 标志位 可选FLAG_PLAY_SOUND表示调整音量时播放声音，FLAG_SHOW_UI表示显示系统的音量对话框
	);
}

/**
 * 获取系统音量
 * @param {Number} mode 音量模式 0-媒体 1-铃声 2-闹钟 3-通知 4-系统 5-通话音量 6-拨号键盘音
*/
const getVolume = function (mode = 0) {
	const modes = [
		AudioManager.STREAM_MUSIC,//媒体（音乐、视频）音量
		AudioManager.STREAM_RING,//铃声音量
		AudioManager.STREAM_ALARM,//闹钟音量
		AudioManager.STREAM_NOTIFICATION,//通知音量
		AudioManager.STREAM_SYSTEM,//系统音量
		AudioManager.STREAM_VOICE_CALL,//通话音量
		AudioManager.STREAM_DTMF//DTMF 音量：电话拨号键盘音
	]
	const audioManager = main.getSystemService(Context.AUDIO_SERVICE);
	const maxVolume = audioManager.getStreamMaxVolume(modes[mode])//获取设备最大音量级别
	const volumeLevel = audioManager.getStreamVolume(modes[mode])//获取设备当前音量级别
	const volume = volumeLevel / maxVolume//根据最大音量级别计算获取到的音量级别
	return volume
}

/**
 * base64图片转化本地图片
 * @param {String} base64 base64图片
 * @param {String} fileName 保存图片名称
*/
const base64ImageToLocalImage = function (base64, fileName) {
	return new Promise((resolve, reject) => {
		const basePath = '_doc'
		const dirPath = 'uniapp_temp'
		const tempFilePath = basePath + '/' + dirPath + '/' + fileName
		const bitmap = new plus.nativeObj.Bitmap(fileName)
		bitmap.loadBase64Data(base64, function() {
		    bitmap.save(tempFilePath, {}, function() {
				resolve(tempFilePath)
		        bitmap.clear()
		    }, function(error) {
		        bitmap.clear()
				reject(error)
		    })
		}, function(error) {
		    bitmap.clear()
			reject(error)
		})
	})
}

/**
 * 保存base64图片到相册
 * @param {String} base64 图片
 * @param {String} fileName 保存图片名称
*/
const saveBase64ImageToAlbum = function (base64, fileName) {
	return new Promise((resolve, reject) => {
		base64ImageToLocalImage(base64, fileName).then(path => {
			plus.gallery.save(path, function (e) {
				resolve(e.path)
			}, function (error) {
				reject(error)
			});
		})
	})
}
/**
 * 打开本应用设置窗口
*/
const openSetting = function () {
	const intent = new Intent(Settings.ACTION_APPLICATION_DETAILS_SETTINGS);
	const uri = Uri.fromParts("package", main.getPackageName(), null);
	intent.setData(uri);
	main.startActivity(intent);
}

/**
 * 展示获取权限告知目的通知
 * @param {String} title 提示标题
 * @param {String} message 提示内容
 * @param {String} height 通知弹窗高度
*/
const showPrivacyNotify = function (title, message, height = '100px') {
	const view = new plus.nativeObj.View('test',
	{top:'50px',left:'10%',height: height,width:'80%'},
	[
		{tag:'rect',id:'rect',color:'#ffffff', rectStyles: {borderWidth: '1px', borderColor: '#000'}, position:{top:'0px',left:'0px',width:'100%',height:'100%'}},
		{tag:'font',id:'title',text: title, textStyles:{size:'18px', align: 'center', color: '#000', weight: 'bold', margin: '10px', verticalAlign: 'top'}},
		{tag:'font',id:'message',text: message, textStyles:{size:'14px', color: '#666', margin: '10px', whiteSpace: 'normal', verticalAlign: 'top'}, position: {top: '30px'}}
	],
	);
	view.show();
	return view
}

module.exports = {
	shareText,
	shareFile,
	openFileSystem,
	setWallpaper,
	requestPermissions,
	uninstall,
	writeDocumentContent,
	setFullscreen,
	downloadNetResourceSaveLocal,
	downloadNetResourceToBase64,
	writeAndExportFile,
	renameAndMoveFile,
	copyFileWithNIO,
	deleteFile,
	deleteFiles,
	setVolume,
	getVolume,
	base64ImageToLocalImage,
	saveBase64ImageToAlbum,
	openSetting,
	showPrivacyNotify
}